#!/usr/bin/env python
'''
The networking components of Butler, allows it to send and recieve
commands to/from other computers.

'''

import re
import socket

DEFAULT_PORT = 29425
HOSTNAME = socket.gethostname()
BUFFER = 1024

#Try to get the external ip of the computer.
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(('google.com', 80))
    HOSTNAME = s.getsockname()[0]
except Exception:
    print "Couldn't get fix on external hostname"

#Butler networking commands

def add_computer(wholeargs, splitargs):
    '''Adds a computer to send/recieve remote commands to Butler's
    list.
    Format:
    add [c[omp[uter]]] ip[:port] [[as] name]
    '''
    #Format: add computer ip name

    #Get the ip.
    p = re.compile(r"""
    \s*                 #Leading whitespace.
    (?P<ip>[\d.]+)      #Ip address (ipv4 only)
    (?P<port>[\d:]*)    #Port number (includes leading :)
    \s*                 #White space
    [as]*               #the word "as" or "sas" or "ass" :P
    \s*                 #White space
    (?P<name>[-.\w]*)   #The name of a computer (may include - or .)
    """, re.VERBOSE)
    m = p.match(wholeargs)

    if not m:
        print ("Error, couldn't parse the machine you entered.")
        return

    ip = m.group('ip')

    name = m.group('name') if m.group('name') else ip
    port = m.group('port')[1:] if m.group('port') else DEFAULT_PORT

    print ("Added machine '%s' at IP: %s:%s to known machines." % (name, ip, port))

    ck = registry.fetch("butler.networking.computers_known", {})
    ck[name] = (ip, port)
    registry.store("butler.networking.computers_known", ck)


def start_server(a, arglist):
    #Starts the butler server on the specified port.
    port = DEFAULT_PORT
    print arglist
    if arglist:
        try:
            port = int(arglist[0])
        except:
            pass

    print "Listening on %s:%s" % (HOSTNAME, port)

    addr = (HOSTNAME, port)

    # Create socket and bind to address
    UDPSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    UDPSock.bind(addr)
    while 1: #Unlimited connections.
        while 1: #Unlimited data per connection.
            data,addr = UDPSock.recvfrom(BUFFER)
            if not data:
                break
            else:
                #TODO check if computer is on trusted list.
                butler.command(data)

    print "exited"

start_server.isdaemon = True  #Don't wait for this thread to end.

def send_command(compname, command_to_send):
    ck = registry.fetch("butler.networking.computers_known", {})
    try:
        (ip, port) = ck[compname]
        UDPSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        UDPSock.sendto(command_to_send, (ip, int(port)))
        UDPSock.sendto("", (ip, int(port)))
        UDPSock.close()
        print "Sent command to %s" % (compname)
    except Exception, e:
        print "Sendnig command failed %s." % (e)


def known_machines(a,b):

    ck = registry.fetch("butler.networking.computers_known", {})

    print("Name:\t\tIP:\t\tPort:")

    for i in ck.keys():
        ip, port = ck[i]
        print("%s\t\t%s\t%s" % (i, ip, port))


TRIGGERS = {
            "add":add_computer,
            "add c":add_computer,
            "known":known_machines,
            "known c":known_machines,
            "start net":start_server,
            }

def SETUP():
    butler.networking_send = send_command
